package com.atguigu.exam.service.impl;

import com.atguigu.exam.common.CacheConstants;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.entity.QuestionAnswer;
import com.atguigu.exam.entity.QuestionChoice;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.question.QuestionQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.io.input.TaggedReader;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private QuestionChoiceMapper choiceMapper;

    @Autowired
    private QuestionAnswerMapper answerMapper;

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public void queryPage(Page<Question> questionPage, QuestionQueryVo questionQueryVo) {

        LambdaUpdateWrapper<Question> querywrapper = new LambdaUpdateWrapper<>();
        //题目分类ID查询
        querywrapper.eq(questionQueryVo.getCategoryId() != null, Question::getCategoryId,
                questionQueryVo.getCategoryId());
        //题目难度查询
        querywrapper.eq(!ObjectUtils.isEmpty(questionQueryVo.getDifficulty()),
                Question::getDifficulty, questionQueryVo.getDifficulty());
        //题目类型查询
        querywrapper.eq(!ObjectUtils.isEmpty(questionQueryVo.getType()),
                Question::getType, questionQueryVo.getType());
        //题目标题查询
        querywrapper.like(!ObjectUtils.isEmpty(questionQueryVo.getKeyword()),
                Question::getTitle, questionQueryVo.getKeyword());
        //根据创建时间排序
        querywrapper.orderByDesc(Question::getCreateTime);

        page(questionPage, querywrapper);

        fillQuestionsDetailsBatch(questionPage.getRecords());
    }

    @Override
    public Question getQuestionWithDetail(Long id) {
        Question question = this.getById(id);
        if (question == null) return null;

        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> questionChoices = choiceMapper.selectList(
                    new QueryWrapper<QuestionChoice>().eq("question_id", id));
            question.setChoices(questionChoices);
        }

        QuestionAnswer questionAnswers = answerMapper.selectOne(
                new QueryWrapper<QuestionAnswer>().eq("question_id", id));

        question.setAnswer(questionAnswers);

        try {
            new Thread(() -> incrementQuestionViewCount(id)).start();
        } catch (Exception e) {
            log.warn("增加题目访问计数失败，但不影响正常业务，题目ID: {}", id);
        }

        return question;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveQuestionWithDetails(Question question) {

        this.save(question);

        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choices = question.getChoices();
            if (!CollectionUtils.isEmpty(choices)) {
                StringBuilder correctAnswer = new StringBuilder();
                for (int i = 0; i < choices.size(); i++) {
                    QuestionChoice choice = choices.get(i);
                    choice.setQuestionId(question.getId());
                    choiceMapper.insert(choice);
                    if (choice.getIsCorrect() != null && choice.getIsCorrect()) {
                        if (correctAnswer.length() > 0) {
                            correctAnswer.append(",");
                        }
                        correctAnswer.append((char) ('A' + i));
                    }
                }

                if (correctAnswer.length() > 0) {
                    QuestionAnswer answer = new QuestionAnswer();
                    answer.setQuestionId(question.getId());
                    answer.setAnswer(correctAnswer.toString());
                    answerMapper.insert(answer);
                }
            }

        } else {
            QuestionAnswer answer = question.getAnswer();
            if (answer != null) {
                answer.setQuestionId(question.getId());
                answerMapper.insert(answer);
            }
        }


    }


    @Override
    public void updateQuestionWithDetails(Question question) {
        this.updateById(question);
        choiceMapper.delete(new QueryWrapper<QuestionChoice>().eq("question_id", question.getId()));
        answerMapper.delete(new QueryWrapper<QuestionAnswer>().eq("question_id", question.getId()));
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choices = question.getChoices();
            if (!CollectionUtils.isEmpty(choices)) {
                StringBuilder daan = new StringBuilder();
                for (int i = 0; i < choices.size(); i++) {
                    QuestionChoice choice = choices.get(i);
                    choice.setQuestionId(question.getId());
                    choice.setId(null);
                    choiceMapper.insert(choice);
                    if (choice.getIsCorrect() != null && choice.getIsCorrect()) {
                        if (daan.length() > 0) {
                            daan.append(",");
                        }
                        daan.append((char) ('A' + i));
                    }
                }
                if (daan.length() > 0) {
                    QuestionAnswer answer = new QuestionAnswer();
                    answer.setQuestionId(question.getId());
                    answer.setAnswer(daan.toString());
                    answerMapper.insert(answer);
                }
            }
        } else {
            QuestionAnswer answer = question.getAnswer();
            if (answer != null) {
                answer.setQuestionId(question.getId());
                answerMapper.insert(answer);
            }
        }


    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deleteQuestion(Long id) {
        LambdaQueryWrapper<PaperQuestion> queryWrapper = new LambdaQueryWrapper<>();
        paperQuestionMapper.delete(queryWrapper.eq(PaperQuestion::getQuestionId, id));
        Long count = paperQuestionMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new RuntimeException("题目有试卷引用，删除失败！");
        }
        choiceMapper.delete(new QueryWrapper<QuestionChoice>().eq("question_id", id));
        answerMapper.delete(new QueryWrapper<QuestionAnswer>().eq("question_id", id));
        boolean issuccess = removeById(id);
        return issuccess;
    }

    @Override
    public List<Question> getPopularQuestions(Integer size) {

        List<Question> questionList = new ArrayList<>();

        Set<Object> popularQuestionIds = redisUtils.zReverseRange(CacheConstants.QUESTION_VIEW_COUNT_KEY, 0, size - 1);
        Set<Object> safeQuestionIds = Objects.requireNonNullElse(popularQuestionIds, Collections.emptySet());
        log.info("Redis 热榜查询到的有效Id数量：{}", safeQuestionIds.size());

        if (size > 0 && safeQuestionIds.isEmpty()) {
            List<Long> questionIds = safeQuestionIds.stream()
                    .map(id -> Long.valueOf(id.toString()))
                    .collect(Collectors.toList());

            List<Question> redisquestionList = Optional.ofNullable(listByIds(questionIds)).orElse(Collections.emptyList());
            questionList.addAll(redisquestionList);
        }

        int needMore = Math.max(0, size - questionList.size());
        if (needMore > 0) {
            List<Long> existIds = questionList.stream()
                    .map(Question::getId)
                    .collect(Collectors.toList());

            LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();

            if (!existIds.isEmpty()) {
                queryWrapper.notIn(Question::getId, existIds);
            }

            queryWrapper.orderByDesc(Question::getCreateTime).last("LIMIT " + needMore);

            List<Question> list = this.list(queryWrapper);
            questionList.addAll(list);

        }

        fillQuestionsDetailsBatch(questionList);
        return questionList;

    }

    //预览excel文件内容
    @Override
    public List<QuestionImportVo> previewData(MultipartFile file) throws IOException {
        if (file.isEmpty()){
            throw new RuntimeException("文件不能为空");
        }
        String filename = file.getOriginalFilename();
        if (filename==null || (!filename.endsWith(".xlsx") && !filename.endsWith(".xls"))){
            throw new RuntimeException("请上传Excel文件（.xlsx或.xls格式）");
        }
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);

        return questionImportVoList;
    }

    @Override
    public String batchImportQuestions(MultipartFile file) throws IOException {
        if(file.isEmpty()){throw new RuntimeException("文件不能为空");}
        String filename = file.getOriginalFilename();
        if (filename==null || (!filename.endsWith(".xlsx") && !filename.endsWith(".xls"))){
            throw new RuntimeException("请上传Excel文件（.xlsx或.xls格式）");
        }
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        if (questionImportVoList.isEmpty()){throw new RemoteException("Excel文件中没用有效数据");}

        int successCount=0;

        for (QuestionImportVo questionImportVo : questionImportVoList) {
            try {
                Question question=convertImportDtoToQuestion(questionImportVo);
                saveQuestionWithDetails(question);
                successCount++;
                log.info("成功导入题目",question.getTitle());
            }catch (Exception e){
                log.error("导入题目sb",questionImportVo.getTitle(),e.getMessage());
            }
        }
        String message = String.format("Excel导入完成！成功导入 %d / %d 道题目", successCount, questionImportVoList.size());
        return message;
    }

    @Override
    public String batchImportQuestions(List<QuestionImportVo> questionImportVoList) throws IOException {
        if (questionImportVoList.isEmpty()){throw new RemoteException("Excel文件中没有有效的题目数据");}
        int successCount=0;
        for (QuestionImportVo questionImportVo : questionImportVoList) {
            try {
                Question question=convertImportDtoToQuestion(questionImportVo);
                saveQuestionWithDetails(question);
                successCount++;
                log.info("成功导入题目",question.getTitle());
            }catch (Exception e){
                log.error("导入题目失败",questionImportVo.getTitle(),e.getMessage());
            }
        }
        String message = String.format("Excel导入完成！成功导入 %d / %d 道题目", successCount, questionImportVoList.size());
        return message;
    }

    private Question convertImportDtoToQuestion(QuestionImportVo importVo) {

        Question question=new Question();
        question.setTitle(importVo.getTitle());
        question.setType(importVo.getType());
        question.setMulti(importVo.getMulti()!=null ? importVo.getMulti() : false);
        question.setCategoryId(importVo.getCategoryId()!=null?importVo.getCategoryId():1L);
        question.setDifficulty(importVo.getDifficulty()!=null?importVo.getDifficulty():"MEDIUM");
        question.setScore(importVo.getScore()!=null?importVo.getScore():5);
        question.setAnalysis(importVo.getAnalysis());
        question.setCreateTime(new Date());
        question.setUpdateTime(new Date());

        if ("CHOICE".equals(importVo.getType())&&!CollectionUtils.isEmpty(importVo.getChoices())){
            List<QuestionChoice> choices=new ArrayList<>();
            for (QuestionImportVo.ChoiceImportDto choiceImportDto : importVo.getChoices()) {
                QuestionChoice choice=new QuestionChoice();
                choice.setContent(choiceImportDto.getContent());
                choice.setIsCorrect(choiceImportDto.getIsCorrect()!=null?choiceImportDto.getIsCorrect():false);
                choice.setSort(choiceImportDto.getSort()!=null?choiceImportDto.getSort():choices.size()+1);
                choices.add(choice);
            }
            question.setChoices(choices);
        }else {
            QuestionAnswer answer=new QuestionAnswer();
            answer.setAnswer(importVo.getAnswer());
            answer.setKeywords(importVo.getKeywords());
            question.setAnswer(answer);
        }

        return question;

    }


    private void fillQuestionsDetailsBatch(List<Question> questions) {

        if (questions == null || questions.isEmpty()) return;

        List<Long> ids = questions.stream().map(Question::getId).toList();

        List<QuestionChoice> questionChoices = choiceMapper.selectList(
                new LambdaQueryWrapper<QuestionChoice>().in(QuestionChoice::getQuestionId, ids));

        List<QuestionAnswer> questionAnswers = answerMapper.selectList(
                new LambdaQueryWrapper<QuestionAnswer>().in(QuestionAnswer::getQuestionId, ids));

        Map<Long, List<QuestionChoice>> choiceMap =
                questionChoices.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));


        Map<Long, QuestionAnswer> answerMap =
                questionAnswers.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, a -> a));


        for (Question q : questions) {
            if ("CHOICE".equals(q.getType())) {
                List<QuestionChoice> choices = choiceMap.getOrDefault(q.getId(), new ArrayList<>());
                choices.sort(Comparator.comparingInt(c -> c.getSort() == null ? 0 : c.getSort()));
                q.setChoices(choices);
            }
            q.setAnswer(answerMap.get(q.getId()));
        }
    }


    private void incrementQuestionViewCount(Long questionId) {
        if (!getBaseMapper().exists(new LambdaQueryWrapper<Question>().eq(Question::getId, questionId))) {
            log.warn("尝试增加不存在的题目的访问计数，题目ID：{}", questionId);
        }

        Double newScore = redisUtils.zIncrementScore(CacheConstants.QUESTION_VIEW_COUNT_KEY, questionId, 1);
        log.debug("题目访问计数增加，题目ID：{},当前计数：{}", questionId, newScore.intValue());
    }


}